/*
 * Copyright (c) 2005-2006 Crossbow Technology, Inc.
 * All rights reserved.
 *
 * Use, copy, modification, reproduction and distribution of
 * this software and documentation are governed by the
 * Crossbow Technology End User License Agreement.
 * To obtain a copy of this Agreement, please contact
 * Crossbow Technology, 4145 N. First St., San Jose, CA 95134.
 *
 * Adapted from tinyos-1.x HumTemp
 */

includes trace;

module SHTM {
  provides {
    interface StdControl;
    interface ADC as TempSensor;
    interface ADC as HumSensor;
  }
  uses {
    interface Leds;
    interface Timer;
    interface PXA27XGPIOInt as ADCCompleteInt;
  }

}
implementation {


#include "sht_config.h"

  //states
  enum {READY=0, TEMP_MEASUREMENT=1, HUM_MEASUREMENT=2, POWER_OFF};

  char state;
  uint8_t timeout;
  uint16_t data;

  void HUMIDITY_MAKE_DATA_OUTPUT() { TOSH_MAKE_I2C_BUS1_SDA_OUTPUT();}
  void HUMIDITY_MAKE_DATA_INPUT() { TOSH_MAKE_I2C_BUS1_SDA_INPUT(); }  
  
  void HUMIDITY_SET_DATA() { TOSH_SET_I2C_BUS1_SDA_PIN(); }
  void HUMIDITY_CLEAR_DATA() { TOSH_CLR_I2C_BUS1_SDA_PIN(); }
  char HUMIDITY_GET_DATA() { return TOSH_READ_I2C_BUS1_SDA_PIN(); }  
  
  void HUMIDITY_MAKE_CLOCK_OUTPUT() { TOSH_MAKE_I2C_BUS1_SCL_OUTPUT(); }
  void HUMIDITY_MAKE_CLOCK_INPUT()  { TOSH_MAKE_I2C_BUS1_SCL_INPUT(); }
  void HUMIDITY_SET_CLOCK() { TOSH_SET_I2C_BUS1_SCL_PIN(); }
  void HUMIDITY_CLEAR_CLOCK() { TOSH_CLR_I2C_BUS1_SCL_PIN(); }

  void TOSH_wait_250ns()
      {
          int i;
          for( i=0;i<2; i++)
              {
                  asm volatile ("nop" ::);
              }
      }
  
  char calc_crc(char current, char in) {
    return crctable[current ^ in];
  }

  static inline void  ack()
  {
    HUMIDITY_MAKE_DATA_OUTPUT();
    HUMIDITY_CLEAR_DATA();
    TOSH_wait_250ns();
    HUMIDITY_SET_CLOCK();
    TOSH_wait_250ns();
    HUMIDITY_CLEAR_CLOCK();
    HUMIDITY_MAKE_DATA_INPUT();
    HUMIDITY_SET_DATA();
  }

  static inline void initseq()
  { 
    HUMIDITY_MAKE_DATA_OUTPUT();
    HUMIDITY_SET_DATA();
    HUMIDITY_CLEAR_CLOCK();   
    TOSH_wait_250ns();         
    HUMIDITY_SET_CLOCK();
    TOSH_wait_250ns();
    HUMIDITY_CLEAR_DATA();
    TOSH_wait_250ns();
    HUMIDITY_CLEAR_CLOCK();
    TOSH_wait_250ns();
    HUMIDITY_SET_CLOCK();
    TOSH_wait_250ns(); 
    HUMIDITY_SET_DATA();
    TOSH_wait_250ns(); 
    HUMIDITY_CLEAR_CLOCK();
  }

  static inline void reset()
  {
    int i;
    HUMIDITY_MAKE_DATA_OUTPUT();
    HUMIDITY_SET_DATA();
    HUMIDITY_CLEAR_CLOCK();
    for (i=0;i<9;i++) {
      HUMIDITY_SET_CLOCK();
      TOSH_wait_250ns();
      HUMIDITY_CLEAR_CLOCK();
    }
  }

  static inline char processCommand(int cmd)
  {
    int i;
    int CMD = cmd;
    cmd &= 0x1f;
    reset();           
    initseq();        //sending the init sequence
    for(i=0;i<8;i++)
        {
            if(cmd & 0x80) 
                HUMIDITY_SET_DATA();
            else 
                HUMIDITY_CLEAR_DATA();
            cmd = cmd << 1 ;
            HUMIDITY_SET_CLOCK();
            TOSH_wait_250ns();              
            TOSH_wait_250ns();              
            HUMIDITY_CLEAR_CLOCK();        
        }
    HUMIDITY_MAKE_DATA_INPUT();
    HUMIDITY_SET_DATA();
    TOSH_wait_250ns();
    HUMIDITY_SET_CLOCK();
    TOSH_wait_250ns();
    if(HUMIDITY_GET_DATA()) 
        { 
            reset(); 
            return 0; 
        }
    TOSH_wait_250ns();
    HUMIDITY_CLEAR_CLOCK();
    if((CMD == TOSH_HUMIDITY_ADDR) || (CMD == TOSH_HUMIDTEMP_ADDR) )
        {
            TOSH_wait_250ns();
            call ADCCompleteInt.enable(TOSH_FALLING_EDGE);
        }
    return 1;
  }
  

  command result_t StdControl.init() {
    state = POWER_OFF;
    call ADCCompleteInt.enable(TOSH_FALLING_EDGE);
    return SUCCESS;
  }

  command result_t StdControl.start() {
    state=READY;
    
    HUMIDITY_CLEAR_CLOCK();
    HUMIDITY_MAKE_CLOCK_OUTPUT();
    HUMIDITY_SET_DATA();
    HUMIDITY_MAKE_DATA_INPUT();
    reset();
    processCommand(TOSH_HUMIDITY_RESET);
 // the following line is for debugging purpose
 // call Timer.start(TIMER_REPEAT, 10000);
    return SUCCESS;
  }

  command result_t StdControl.stop() {
    state = POWER_OFF;
    HUMIDITY_CLEAR_CLOCK();
    HUMIDITY_MAKE_CLOCK_INPUT();
    HUMIDITY_MAKE_DATA_INPUT();
    HUMIDITY_CLEAR_DATA();
    call ADCCompleteInt.disable();
    return SUCCESS;
  }

  default async event result_t TempSensor.dataReady(uint16_t tempData) 
  {
    return SUCCESS;
  }


  default async event result_t HumSensor.dataReady(uint16_t humData) 
  {
    return SUCCESS;
  }


  task void readSensor()
  {
    char i;
    char CRC=0;  
    uint32_t temp, humidity;
    data=0; 
    
    for(i=0;i<15;i++)
        {
            HUMIDITY_SET_CLOCK();   
            TOSH_wait_250ns();
            data |= HUMIDITY_GET_DATA();
            //the last bit of data should not be shifted
            if(i!=15) data = (data<< 1);  
            HUMIDITY_CLEAR_CLOCK();
            if (i%8 == 7) ack();
        }
    for(i=0;i<8;i++){
      HUMIDITY_SET_CLOCK();   
      TOSH_wait_250ns();
      CRC |= HUMIDITY_GET_DATA();
      if(i!=7)CRC = (CRC<< 1);
      HUMIDITY_CLEAR_CLOCK();
    }
    // nack with high as it should be for the CRC ack
    HUMIDITY_MAKE_DATA_OUTPUT();
    HUMIDITY_SET_DATA();          
    TOSH_wait_250ns();
    HUMIDITY_SET_CLOCK();
    TOSH_wait_250ns();
    HUMIDITY_CLEAR_CLOCK();

// we will give them the raw data reading
    if(state==TEMP_MEASUREMENT){
        signal TempSensor.dataReady((uint16_t)data);
    }
    else if(state==HUM_MEASUREMENT) {
        signal HumSensor.dataReady((uint16_t)data);
    }
    state=READY;
  }


  async event void ADCCompleteInt.fired(){
         call ADCCompleteInt.clear();
         TOSH_wait_250ns();
         if( READ_GPIO(100) == 0)
             {
                 //only disable it if we get the one we want...
                 //it seems like there's some stale state or somethin
                 call ADCCompleteInt.disable();
                 post readSensor();
             }
         else
             {
                 call ADCCompleteInt.enable(TOSH_FALLING_EDGE);
             }
         call ADCCompleteInt.clear();
     }

  // no such thing
  async command result_t TempSensor.getContinuousData() {
    return FAIL;
  }

  // no such thing
  async command result_t HumSensor.getContinuousData() {
    return FAIL;
  }

  event result_t Timer.fired() {
    //trace(DBG_USR1, " Sensirion debug timer fired,  \r\n");
    state=TEMP_MEASUREMENT;
    processCommand(TOSH_HUMIDTEMP_ADDR);
    return SUCCESS;
  }

  async command result_t TempSensor.getData()
  {
    if(state!= READY ){
      reset();
    }
    state=TEMP_MEASUREMENT;
    processCommand(TOSH_HUMIDTEMP_ADDR);
    return SUCCESS;
  }

  async command result_t HumSensor.getData()
  {
    if(state!= READY ){
      reset();
    }
    state=HUM_MEASUREMENT;
    processCommand(TOSH_HUMIDITY_ADDR);
    return SUCCESS;
  }
}
